package DBIx::Class::DataValidator;

our $VERSION = '0.05';

=head1 NAME

DBIx::Class::DataValidator - Data validation right in the model


=head1 SYNOPSIS

Set the resultset in your schema:

    package MySchema;
    
    use base qw/ DBIx::Class::Schema /;
    __PACKAGE__->load_namespaces(
        default_resultset_class => '+DBIx::Class::ResultSet::DataValidator'
    );

In your model load the component:

    package MySchema::MyModel;
    
    use base qw/
        DBIx::Class
    /;
    
    __PACKAGE__->load_components( qw/
        Component::DataValidator
        Core
    / );
    
    __PACKAGE__->add_validation_profiles( {
        default => {
            some_column => sub {
                my ( $dv, $value ) = @_;
                return $value eq '' || $value =~ /^\d+$/ || $value =~ /^[a-z]{3}$/;
            }
        }
    } );


=head1 DESCRIPTION

Extends model by validation methods. It works additive, not replacing,
so you can still use the non-validation methods for updating and creating.

The validation is this way much closer to the data definition.

=head1 SUMMARIZE

Before you start reading below, a short introduction of the multiple approaches
and methods you can use for validation.

Most of those could be combined.

=over

=item * Validation profiles

These are ment to be used in most complex validation scenarios. You can validate
the data with multiple profiles (one after another, chained) with one call.

=item * Inline column validation

Directly in the column definition via add_column they are most simple and fast to setup. Also
the definition of the columns is not apart from the definition of the validation.

They are limited and not as powerful as validation profiles.

You can use them in addition to validation profiles in which case they will be executed
before validation profiles.

=item * DBMS specific validation

This is a kind of "auto-validation" for basic datatypes of your column. They are DBMS
dependent. So Oracle DB might have some types which PostgreSQL has not and vice
versa and so on.

They normally apply per default if you dont deactivate them manually (see below). They
are overwritten by your custom inline column validation.

They can be understand as a default inline column validation.

=item * Named validation methods

Those could be used either in inline column validation or validation profiles alike.

This is simply an appraoch to outsource often used validation methods or regular
expressions to reuse them in multiple models and/or for multiple columns.

=item * Validation column classes

Another approach to reduce code in your models by pre-defining.
This includes more then the basic datatypes (you can say each integer column is
an integer-class column) by allowing custom and complex validations.

They can also used for non validation purposes (just dont use the validation
key). Named validation methods are of course usable.

Probably you should define those classes in a super model from which all your
other models inherit to get the most benefit out of it.

=back


=head1 EXPLANATION

=head2 VALIDATION PROFILES

Any validation profile consists of two parts: a user defined name and the
validation definition.

There are three "special" validation profiles which are called "default
validation profiles" because they are always used, if they exist.

All other (non special) validation profiles are called "custom validation profile"
and a not used automatically.

=head3 DEFAULT VALIDATION PROFILES

=over

=item B<default>

This one is used _always_. On each update, update_all and create.

Tou might want to put here some "always applying" validations.

=item B<create>

Is used on each create.

    $model->createl_validated( $data_ref ); # will call default and update

=item B<update>

Is used on each update and update_all.

    $object->update_validated( $data_ref ); # will call default and update
    $result->update_all_validated( $data_ref ); # will call default and update

=cut

=head3 CUSTOM VALIDATION PROFILES

All other validation profiles with other names.

    package MySchema::MyModel;
    # ..
    __PACKAGE__->add_validation_profiles( {
        custom1 => {
            col1 => {
                constraint => sub { .. }
            },
            col2 => {
                required => 1
                constraint => sub { .. }
            }
        },
        custom2 => {
            col1 => {
                constraint => sub { .. }
            },
            col3 => {
                required => 1
            }
        }
    } );

    # call default, custom1 and custom2 validators
    $object->update_validated( $data_ref, qw/ default custom1 custom2 / );

The validators work additive, means first all validation methods from
default-validator will be used. If all pass, all validators from 
custom1 will be used, if all pass all validation methods from
custom2 will be used and so on..


=head2 INLINE COLUMN VALIDATORS

Column validators can come in handy if you want the validators directly
at your column definition.


    __PACKAGE__->add_columns(
        col1 => {
            # regular column definition
            validation => {
                required => 1,
                constraint => sub { my ( $dv, $value ) = @_; return $value eq '123' }
            }
        },
        qw/
            non validated columns
        /
    );

Another likely usage is if sharing the same columns between multiple
models (eg inheriting).

    package MyColumns;
    
    our @DEFAULT_COLUMNS = (
        some_column => {
            # regular column definition
            validation => {
                required   => 1,
                constraint => qr~^\d+\$~, # normal Data::FormValidator-constraint!
            }
        },
        other_column => {
            # regular column definition
            validation => {
                required   => 0, # or dont provide required at all ..
                constraint => sub {
                    my ( $validator_result, $value ) = @_;
                    return $value eq 'ok';
                }
            }
        },
        yet_another_column => {
            # regular column definition
            validation => {
                required   => 0, # or dont provide required at all ..
                constraint => 'named:method_name'
            }
        }
    );

Now use the in your model:

    package MySchema::MyModel1;
    
    use MyColumns;
    
    __PACKAGE__->add_columns(
        @MyColumns::DEFAULT_COLUMNS,
        qw/ other columns /
    );

And in another model:

    package MySchema::MyModel2;
    
    use MyColumns;
    
    __PACKAGE__->add_columns(
        @MyColumns::DEFAULT_COLUMNS,
        qw/ foo bar /
    );


=head2 WORKING WITH DBMS SPECIFIC VALIDATION

DBMS specific validation is an approach to validate at least the absolute minimum
for input values. Eg an integer column could never take a non-numeric string as
input.

Per default DBMS validation is enabled and tries to load your storage
engine specific validation module (so far only a base class, which validates all
common integer and float datatypes as well as characters (length)).

All your custom inline column validation OVERWRITES the DBMS specific validation (if
you define an inline validation for an integer column "col1" you have to check
wheter it is integer or not yourself).

Validation profiles are additive.

Column classes are not affected by this, as long as you dont provide explicit
validation with them (then they behave as inline column validation).

You can disable DBMS specific validation by setting this:

    package MySchema::MyModel;
    
    __PACKAGE__->disable_native_validation( 1 );

Again: use either in parent packages from which all your models inherit from or
in single models.

In DBIC 0.9 something like this will be in the core and probably removed from this module!


=head2 NAMED VALIDATION METHODS

The validations methods you apply in the profiles (add_validation_profiles) and inline
(add_columns) are sometimes to "static" to fit application needs.

Therefore you can use named methods.

You use them as constraints. Instead of a regular expression or a code-ref
you simple write "named:method_name".

    package MySchema::MyModel;
    
    __PACKAGE__->add_columns(
        column_name => {
            # regular column definition
            validation => {
                required   => 0,
                constraint => 'named:method_name'
            }
        }
    );

These named methods have to be mapped to real methods in the model (or a super model you
are inheriting from):

    package MyModel;
    # ..
    
    # set named method for module
    __PACKAGE__->add_validation_methods( {
        method_name => sub { .. }
    } );

For even more flexibel validation (defined a runtime) you could temporarily overwrite
or add a validation method:

    # later in your code, overwrite the definition
    my $object = $schema->resultset( 'MyModel' )->find( $id );
    $object->set_temporary_validation_methods( {
        # same name:
        method_name => sub {
            # do other thing
        }
    } );
    
    # run the update which will use the new method!
    $object->update( $some_data_ref );


=head3 CAUTION

This only work on update (not update_all, not create)!

The temporary method will be wiped after first usage!

    # after update the tempoary method will be "wiped" AFTER FIRST USAGE!
    $object->update_validated( $data_ref );
    
    # .. and the "old" method will be used again ..
    $object->update_validated( $data_ref );


=head2 VALIDATION WITH COLUMN CLASSES

Column classes are an approach to minimize the redundancy of defining the same 
validation for the same type of columns.

For example, if you have a character column where you save CSV and you
want it always look like "yadda;yadda;yadda" .. 3 columns, separated by
";", then you could do this:

    __PACKAGE__->add_validated_column_class( {
        my_csv_column => {
            default_value => ';;',
            validation    => {
                constraint => qr~^([^;]+?);([^;]+?);([^;]+?)$~
            }
        }
    } );
    __PACKAGE__->add_columns( {
        my_column1 => 'class:my_csv_column',
        my_column2 => 'class:my_csv_column'
    } );

(you can use this also for non validation purposes)


=head3 CAUTION

You have to register the column classes BEFORE you add the columns. So either
do this in a super-class for you model or, as in the example, before the
actual add_columns call.



=head1 USAGE EXAMPLES

You always have to load both: the component-module and the resultset-module. You
can do this either "globally" in super-modules from which you inherit or within
a single model.


=head2 ALREADY HAVING CUSTOM RESULT SET

If you have your own ResultSet, some kind like this:

    package MySchema::ResultSet::MyResultSet;
    
    use base qw/ DBIx::Class::ResultSet /;

simply use the DBIx::Class::ResultSet::DataValidator-Module as base:

    package MySchema::ResultSet::MyResultSet;
    
    use base qw/ DBIx::Class::ResultSet::DataValidator /;


=head2 USING RESULTSET IN A SINGLE CLASS

    package MySchema::MyModel;
    
    use base qw/ DBIx::Class /;
    __PACKAGE__->resultset_class( qw/ DBIx::Class::ResultSet::DataValidator / );


=head2 USING COMPONENT FOR ALL YOUR MODELS

Create a custom default class

    package MySchema::MyDefaultClass;
    
    use base qw/ DBIx::Class /;
    
    # load your components and this
    __PACKAGE__->load_components( qw/
        Component::DataValidator
        Core
    / );

and make all your models inherit from this

    package MySchema::MyModel;
    
    use base qw/ MySchema::MyDefaultClass /;


=head2 USING DBIx::Class::Schema::Loader

If you use L<DBIx::Class::Schema::Loader> instead of L<DBIx::Class::Schema> in your
schema, the load_components in your models will simply be ignored..

Thus you have to load the components and resultset base class in the schema:

    package MySchema;
    
    use base qw/ DBIx::Class::Schema::Loader /;
    use DBIx::Class::ResultSet::DataValidator;
    __PACKAGE__->loader_options(
        components => [ qw/ Component::DataValidator / ],
        result_base_class => 'DBIx::Class::ResultSet::DataValidator'
    );


=head1 HANDLING VALIDATION EXCEPTIONS

If the update or create failes the whole thing will throw an exception (dies..).
This is why you should call any "update_validated" or "create_validated" or
"update_all_validated" always in an "eval {}"-code block.

    use Scalar::Util qw/ blessed /;
    
    eval {
        $object->update_validated( $data_ref );
    };
    
    # having an exception in update
    if ( my $e = $@ ) {
    
        # this is an exception:
        if ( blessed( $e ) && $e->isa( 'DBIx::Class::DataValidator::Exception' ) ) {
            my @error_fields = $e->invalid_and_missing();
            
            # having errors in validation means always: either invalid and/or missing
            #   fields .. 
            if ( @error_fields ) {
                warn 'Errors in fields: '. join( ', ', @error_fields );
            }
            
            # not error fields -> other reason .. maybe DBIx::Class::Exception or
            #   some typo or whatever ..
            else {
                warn "Ooops: ". $e->message;
            }
        }
    }

If you want to handle those exceptions on "application"-level, where you maybe
even dont know wheter this was an update or create :

    if ( $e->isa( 'DBIx::Class::DataValidator::CreateException' ) ) {
        warn "Error in create";
    }
    elsif ( $e->isa( 'DBIx::Class::DataValidator::UpdateException' ) ) {
        warn "Error in update";
    }
    elsif ( $e->isa( 'DBIx::Class::DataValidator::Exception' ) ) {
        warn "Some database error";
    }


=head1 WHAT ABOUT DEFLATE ? 

There is the 'deflate_before_validation' which enables deflate _before_ validation for all columns. 

    package MyModel;
    
    use base qw/ DBIx::Class /;
    # ..
    __PACKAGE__->deflate_before_validation( 1 );

If you dont want deflatation of all your columns but some you could enable it in the column definition

    package MyModel;
    
    use base qw/ DBIx::Class /;
    # ..
    __PACKAGE__->deflate_before_validation( 0 );
    
    __PACKAGE__->add_columns( 
        some_column => {
            # regular definition ..
            validation => {
                constraint     => sub { .. },
                deflate_before => 1
            }
        }
    );

In both scenarios deflate will run twice! Once before validation and once before the actual save! You have to assure your deflate methods can handle "correct" (already deflated) data as well.

=head1 SEE ALSO

=over

=item * L<Data::DBIx::Class>

=item * L<Data::DBIx::Class::Results>

=item * L<Data::DBIx::Class::Schema>

=item * L<Data::DBIx::Class::Validation>

=item * L<Data::FormValidator>

=item * L<Data::FormValidator::Results>

=back

=head1 CONTRIBUTION FROM / THANKS TO

Rob Kinyon

=head1 AUTHOR

Ulrich Kautz - uk@fortrabbit.de

=cut


1;
